package cn.lxh.blog.service.impl;


import cn.lxh.blog.domain.ArticleType;
import cn.lxh.blog.mapper.ArticleTypeMapper;
import cn.lxh.blog.query.ArticleTypeQuery;
import cn.lxh.blog.service.IArticleTypeService;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.CacheConfig;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import java.util.*;

/**
 * ArticleTypeServiceImpl业务层类
 */
@Service // 该类交给spring管理
@Transactional(readOnly = true, propagation = Propagation.SUPPORTS)
// 将数据库设置成只读数据库, 此时若要进行写的操作, 会出现错误, 有事务就以事务方式执行, 没有事务就以非事务方式执行
@CacheConfig(cacheNames = "articleType")
public class ArticleTypeServiceImpl implements IArticleTypeService {

    @Autowired
    private RedisTemplate redisTemplate;

    @Autowired
    private ArticleTypeMapper articleTypeMapper;

    /**
     * 添加文章类型
     *
     * @param articleType
     */
    @Transactional // 开始事务
    @Override
    @CacheEvict(key = "'typeTree'")
    public void add(ArticleType articleType) {
        // 从Redis中删除指定的key
//        redisTemplate.delete("typeTree");
        articleTypeMapper.add(articleType);
    }

    /**
     * 根据id删除文章类型
     *
     * @param id
     */
    @Transactional // 开始事务
    @Override
    @CacheEvict(key = "'typeTree'")
    public void delete(Long id) {
        // 从Redis中删除指定的key
//        redisTemplate.delete("typeTree");
        articleTypeMapper.delete(id);
    }

    /**
     * 修改文章类型
     *
     * @param articleType
     */
    @Transactional // 开始事务
    @Override
    @CacheEvict(key = "'typeTree'")
    public void update(ArticleType articleType) {
        // 从Redis中删除指定的key
//        redisTemplate.delete("typeTree");
        // 设置修改时间
        articleType.setUpdateTime(new Date());
        articleTypeMapper.update(articleType);
    }

    /**
     * 根据id查询单个文章类型
     *
     * @param id
     * @return
     */
    @Override
    public ArticleType findOne(Long id) {
        return articleTypeMapper.findOne(id);
    }

    /**
     * 查询所有文章类型
     *
     * @return
     */
    @Override
    public List<ArticleType> findAll() {
        return articleTypeMapper.findAll();
    }

    /**
     * 根据id批量删除文章类型
     *
     * @param ids
     */
    @Transactional // 开始事务
    @Override
    @CacheEvict(key = "'typeTree'")
    public void patchDelete(Long[] ids) {
        // 从Redis中删除指定的key
//        redisTemplate.delete("typeTree");
        articleTypeMapper.patchDelete(ids);
    }

    /**
     * 分页查询
     *
     * @param articleTypeQuery
     * @return
     */
    @Override
    public PageInfo<ArticleType> queryPage(ArticleTypeQuery articleTypeQuery) {
        // 开启分页, 它只对紧挨着下面的第一个查询所有的sql语句生效(1. 先查询总数量total 2. 根据application.yml中驱动名自动实现分页,如果是mysql就用limit
        // 3. 自动计算limit的第一个值: limit a, b
        PageHelper.startPage(articleTypeQuery.getCurrentPage(), articleTypeQuery.getPageSize());
        // 查询所有
        List<ArticleType> list = articleTypeMapper.queryPage(articleTypeQuery);
        // 返回对象中有: list当前页分页数据和total总条数
        return new PageInfo<>(list);
    }

    /**
     * 获取类型数
     *
     * @return
     */
    private List<ArticleType> getTypeTree() {
        // 创建一个list集合用于保存类型树数据
        List<ArticleType> typeTree = new ArrayList<>();
        // 查询所有文章类型
        List<ArticleType> articleTypeList = articleTypeMapper.findAll();
        // 创建一个map集合, 用于保存文章类型, key为文章类型id, value为文章类型对象
        Map<Long, ArticleType> articleTypeMap = new HashMap<>();
        // 遍历articleTypeList集合
        for (ArticleType articleType : articleTypeList) {
            // 将文章类型对象存入map集合
            articleTypeMap.put(articleType.getId(), articleType);
        }

        // 遍历articleTypeList集合
        for (ArticleType articleType : articleTypeList) {
            // 判断文章类型对象是否有父类型id
            if (articleType.getParentId() == null) { // 顶级类型
                // 顶级类型直接放入typeTree集合中
                typeTree.add(articleType);
            } else { // 有父类型id
                // 获取父类型id
                Long parentId = articleType.getParentId();
                // 根据parentId查询父类型对象
                ArticleType parentType = articleTypeMap.get(parentId);
                // 判断parentType是否为null
                if (parentType != null) { // 防止parentType为null
                    // 获取父类型对象的子类型集合
                    List<ArticleType> children = parentType.getChildren();
                    // 将循环的文章类型对象放进该父类型对象的子类型集合中
                    children.add(articleType);
                }
            }
        }
        return typeTree;
    }

    /**
     * 获取类型数
     *
     * @return
     */
    @Override
    @Cacheable(key = "'typeTree'")
    public List<ArticleType> typeTree() {
/*        // 直接从Redis中取
        Object obj = redisTemplate.opsForValue().get("typeTree");
        if (obj == null) { // Redis中查询不到
            // 从数据库取
            List<ArticleType> typeTree = getTypeTree();
            // 再将typeTree存入Redis
            redisTemplate.opsForValue().set("typeTree", typeTree);
            return typeTree;
        }
        // 在Redis中查询到了, 直接返回
        return (List<ArticleType>) obj;*/
        return getTypeTree();
    }
}
